﻿// <author>Netmon Cipher Dev Team</author>
// <date>2009-07-16</date>
// <version>1.0</version>
// <summary>This contains the entry point Main function.</summary>

namespace SSLDecryptionExpert
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Reflection;
    using Microsoft.NetworkMonitor;

    /// <summary>
    /// SSL Decryption Class
    /// </summary>
    public partial class SSLDecryption
    {
        #region MemberVariables

        /// <summary>
        /// Used to hold the Parser Error Callback Function Pointer.
        /// </summary>
        private static ParserCallbackDelegate errorCallback =
            new ParserCallbackDelegate(ParserCallback);

        /// <summary>
        /// object to hold Current State Enum.
        /// </summary>
        private CurrentState currentState = CurrentState.Initial;

        /// <summary>
        /// Stores the Selected Cipher Suite Information.
        /// </summary>
        private Variables.CipherSuiteInfo cipherSuiteInfo;

        /// <summary>
        /// Set once initialization of Decryption Variables for Netmon Filter Set is complete
        /// </summary>
        private bool initNonReassembly = false;

        /// <summary>
        /// Set once initialization of Decryption Variables for reassembly Netmon Filter Set is complete
        /// </summary>
        private bool initReassembly = false;

        /// <summary>
        /// Netmon Filter Set Object.
        /// </summary>
        private NMFilters filter;

        /// <summary>
        /// Stores Conversation Filter ID.
        /// </summary>
        private uint conversationFilterId = 0;

        /// <summary>
        /// Stores Conversation Filter ID for TLS/SSL Fragmentation Eval Parser.
        /// </summary>
        private uint evalConversationFilterId = 0;
        
        /// <summary>
        /// Stores Display Filter ID.
        /// </summary>
        private uint displayFilterId = 0;

        /// <summary>
        /// Stores SSLVersion Filter ID.
        /// </summary>
        private uint SSLVersionId = 0;

        /// <summary>
        /// Encrypted Capture File Path
        /// </summary>
        private string encryptedCapFile = string.Empty;

        /// <summary>
        /// Decrypted Cature File Path
        /// </summary>
        private string decryptedCapFile = string.Empty;

        /// <summary>
        /// Encrypted Capture File
        /// </summary>
        private IntPtr captureFile = IntPtr.Zero;        

        /// <summary>
        /// Decrypted Capture File
        /// </summary>
        private IntPtr createdCaptureFile = IntPtr.Zero;

        /// <summary>
        /// Conversation Filter String
        /// </summary>
        private string conversationFilter = string.Empty;

        /// <summary>
        /// to store certificate file path.
        /// </summary>
        private string certificateFilePath;

        /// <summary>
        /// to store certificate Password
        /// </summary>
        private string certificatePassword;

        /// <summary>
        /// NPL Parser Handle
        /// </summary>
        private IntPtr nplParser = IntPtr.Zero;
        
        /// <summary>
        /// Frame Parser Handle
        /// </summary>
        private IntPtr frameParser = IntPtr.Zero;

        /// <summary>
        /// Frame Parser Configuration Handle
        /// </summary>
        private IntPtr frameParserConfig = IntPtr.Zero;

        /// <summary>
        /// PaulLo: Another Frame Parser Handle to look for TLS/SSL fragmentation
        /// </summary>
        private IntPtr evalframeParser = IntPtr.Zero;

        /// <summary>
        /// PaulLo: AnotherFrame Parser Configuration Handle to look for TLS/SSL fragmentation
        /// </summary>
        private IntPtr evalframeParserConfig = IntPtr.Zero;

        /// <summary>
        /// PaulLo: AnotherFrame Parser Configuration Handle to look for TLS/SSL fragmentation
        /// </summary>
        private UInt32 evalReassembledFrameCount = 0;

        /// <summary>
        /// Raw Frame Handle
        /// </summary>
        private IntPtr rawFrame = IntPtr.Zero;
        
        /// <summary>
        /// Reassembled Frame Handle
        /// </summary>
        private IntPtr reassembledFrame = IntPtr.Zero;
        
        /// <summary>
        /// Parsed Frame Handle
        /// </summary>
        private IntPtr parsedFrame;
        
        /// <summary>
        /// Inserted Raw Frame Handle
        /// </summary>
        private IntPtr insertedRawFrame;

        /// <summary>
        /// Maintains the Frame Source Number
        /// </summary>
        private uint frameSource;

        /// <summary>
        /// Maintains the count of Decrypted Frames Inserted.
        /// </summary>
        private uint framesInserted = 0;

        /// <summary>
        /// Indicates the current decrypted frame number.
        /// </summary>
        private uint decryptedFrameNumber = 0;

        /// <summary>
        /// stores latest status message.
        /// </summary>
        private string message = string.Empty;

        /// <summary>
        /// TRUE if Esp is present.
        /// </summary>
        private bool isEspPresent = false;

        #endregion

        /// <summary>
        /// Delegate to log progress information
        /// </summary>
        /// <param name="statusMessage">Status Message</param>
        public delegate void LogEventHandler(string statusMessage);

        /// <summary>
        /// Event which captures the status message
        /// </summary>
        public event LogEventHandler Log;

        /// <summary>
        /// Updates the status information.
        /// </summary>
        public void UpdateStatusInfo()
        {
            if (this.Log != null)
            {
                if (this.message != Variables.statusMessage)
                {
                    this.message = Variables.statusMessage;
                    this.Log(Variables.statusMessage);
                }
            }
        }

        /// <summary>
        /// Calls the Start Decryption Method
        /// which decrypts the encrypted capture file.
        /// </summary>
        /// <param name="property">Dictionary of Properties.</param>
        /// <param name="decryptionResult">Decryption Result.</param>
        /// <returns>Returns TRUE if Decryption is successful.</returns>
        public bool SslDecryptCapture(
                        Dictionary<string, string> property,
                        out string decryptionResult)
        {
            Variables.incorrectCertificate = false;
            Variables.passwordMismatch = false;

            bool result = false;
            decryptionResult = "failed";

            try
            {
                 result = this.StartDecryption(property, out decryptionResult);
            }
            catch (NullReferenceException e)
            {
                Variables.Log.LogMessage("Exception: " + e.Message + e.StackTrace);
            }

            try
            {
                NetmonAPI.NmCloseHandle(this.frameParser);
                NetmonAPI.NmCloseHandle(this.nplParser);
                NetmonAPI.NmCloseHandle(this.frameParserConfig);
                NetmonAPI.NmCloseHandle(this.rawFrame);
                NetmonAPI.NmCloseHandle(this.reassembledFrame);
                NetmonAPI.NmCloseHandle(this.parsedFrame);
                NetmonAPI.NmCloseHandle(this.insertedRawFrame);
                NetmonAPI.NmCloseHandle(this.captureFile);
                NetmonAPI.NmCloseHandle(this.createdCaptureFile);
                NetmonAPI.NmApiClose();
            }
            catch (BadImageFormatException)
            {
                Variables.Log.LogMessage("Cleanup Failed.  Wrong API Version??");
            }
            catch (DllNotFoundException)
            {
                Variables.Log.LogMessage("Cleanup Failed.  No API Found");
            }
            finally
            {
                if (Variables.Log != null)
                {
                    Variables.Log.CloseLog();
                }
            }

            return result;
        }
        
        /// <summary>
        /// Loads the parserengine, opens the netmoncapture file for reading
        /// creates a new netmon capture file for writing in to it.
        /// </summary>
        /// <param name="property">Dictionary of Properties.</param>
        /// <param name="decryptionResult">Decryption Result.</param>
        /// <returns>Returns TRUE if Decryption is successful.</returns>
        private bool StartDecryption(
                        Dictionary<string, string> property,
                        out string decryptionResult)
        {
            decryptionResult = "Decryption Failed...";
            
            bool conversationFrame = false;
            bool loadedParserEngine = false;
            uint frameCount;            
            uint errorValue;

            ////Reading Config values
            this.InitializeConfigProperties(property);

            Variables.Log.LogMessage("NMDecrypt Version: " + Assembly.GetEntryAssembly().GetName().Version.ToString());

            bool initialized = InitializeNMAPI();
            if (!initialized)
            {
                decryptionResult = "Decryption failed, "
                                  + "While Initializing Netmon APIs Parser Engine";
                return false;
            }

            if (!string.IsNullOrEmpty(Constants.DisplayFilter))
            {
                loadedParserEngine = LoadParsers(
                                            this.conversationFilter,
                                            out this.nplParser,
                                            out this.frameParser,
                                            out this.frameParserConfig);
                if (!loadedParserEngine)
                {
                    decryptionResult = "Decryption failed, While Loading Parser Engine";
                    return false;
                }

                // PaulLo: Create second frame parser to evaluate for TLS/SSL fragmentation
                loadedParserEngine = LoadEvalParser(
                                            this.conversationFilter,
                                            this.nplParser,
                                            out this.evalframeParser,
                                            out this.evalframeParserConfig);
                if (!loadedParserEngine)
                {
                    decryptionResult = "Decryption failed, creating second eval frame parser";
                    return false;
                }
            }

            if (!CheckVersions())
            {
                return false;
            }

            bool opened = OpenCaptureFile(this.encryptedCapFile, out this.captureFile);
            if (!opened)
            {
                decryptionResult = "Decryption failed, While opening the capture file";
                return false;
            }
            
            // Retrieve the number of frames in this capture file                        
            errorValue = NetmonAPI.NmGetFrameCount(this.captureFile, out frameCount);
            if (errorValue != (ulong)Status.ERROR_SUCCESS)
            {
                decryptionResult = "Decryption failed, While getting"
                                   + " frame count of the capture file";
                Variables.Log.LogMessage(decryptionResult);
                return false;
            }

            bool created = CreateCaptureFile(this.decryptedCapFile, out this.createdCaptureFile);
            if (!created)
            {
                decryptionResult = "Decryption failed, "
                                   + "While Creating new capture file";
                return false;
            }

            bool isReassembled = false;
            bool reassemblyFilterSet = false;
            string initFilter = string.Empty;
            string initReassemblyFilter = string.Empty;
            bool isKeyBlockComputed = false;
            bool isNextFrameSSL = false;
            ulong lastTCPID = ulong.MaxValue;
            Stack HandleStack = new Stack();

            // Loop through capture file
            for (uint frameNumber = 0; frameNumber < frameCount; frameNumber++)
            {
                conversationFrame = true; 
                Variables.isMacVerified = true;                
                bool isDecryptedAppDataPacket = false;

                this.rawFrame = IntPtr.Zero;
                this.reassembledFrame = IntPtr.Zero;

                // Reset any previous computed payload lengths.
                Variables.ReassemblyPayloadLength = 0;

                errorValue = NetmonAPI.NmGetFrame(this.captureFile, frameNumber, out this.rawFrame);
                if (errorValue != (ulong)Status.ERROR_SUCCESS)
                {
                    Variables.Log.LogMessage("Error occured while getting" + frameNumber
                        + "frame of encrypted capture file");
                    continue;
                }

                HandleStack.Push(this.rawFrame);

                if (loadedParserEngine)
                {
                    NmFrameParsingOption option = (NmFrameParsingOption.FieldDisplayStringRequired |
                                            NmFrameParsingOption.FieldFullNameRequired |
                                            NmFrameParsingOption.DataTypeNameRequired);

                    errorValue = NetmonAPI.NmParseFrame(
                                    this.frameParser,
                                    this.rawFrame,
                                    frameNumber,
                                    option,
                                    out this.parsedFrame,
                                    out this.insertedRawFrame);

                    if (errorValue != (ulong)Status.ERROR_SUCCESS)
                    {
                        decryptionResult = "Error " + errorValue + " occurred in parsing frame "
                                            + frameNumber;

                        Variables.Log.LogMessage(decryptionResult);
                        return false;
                    }

                    HandleStack.Push(this.parsedFrame);
                    HandleStack.Push(this.insertedRawFrame);

                    ////parameter to check if the frame is having the filterId
                    bool passed = true;

                    NmFragmentationInfo frameFragmentation = new NmFragmentationInfo();
                    errorValue = NetmonAPI.NmGetFrameFragmentInfo(this.parsedFrame, ref frameFragmentation);

                    errorValue = NetmonAPI.NmEvaluateFilter(
                                                this.parsedFrame,
                                                this.displayFilterId,
                                                out passed);

                    if (errorValue != (ulong)Status.ERROR_SUCCESS)
                    {
                        Variables.Log.LogMessage(
                                    "Error occured in retrieving the state"
                                    + "of the Display filter");

                        ReleaseHandles(ref HandleStack);
                        continue;
                    }

                    if (!string.IsNullOrEmpty(this.conversationFilter))
                    {
                        errorValue = NetmonAPI.NmEvaluateFilter(
                                                this.parsedFrame,
                                                this.conversationFilterId,
                                                out conversationFrame);

                        if (errorValue != (ulong)Status.ERROR_SUCCESS)
                        {
                            Variables.Log.LogMessage(
                                        "Error occured in retrieving" +
                                        " the state of the Conversation Filter");

                            ReleaseHandles(ref HandleStack);
                            continue;
                        }
                    }

                    if (isNextFrameSSL && conversationFrame)
                    {
                        passed = true;
                        isNextFrameSSL = false;

                        uint PayloadLength = GetPayloadLength(this.frameParser);

                        // If NextFrame is supposed to be SSL, then make sure it's a fragment and the PayloadLength
                        // is zero.  If it's a fragment, then it shouldn't be part of a Next Frame SSL.  In that
                        // case it's probably an ACK going the other direction.  The other possiblity is that it
                        // was not detected because TLS/SSL fragmentation. In that case, the payload length would
                        // not be zero.
                        if (frameFragmentation.FragmentType == NmFrameFragmentationType.None && PayloadLength == 0)
                        {
                            passed = false;
                            isNextFrameSSL = true;
                        }
                    }

                    if (passed && conversationFrame)
                    {
                        string fragProt = new string(frameFragmentation.FragmentedProtocolName).Replace("\0", string.Empty);
                        if ((frameFragmentation.FragmentType == NmFrameFragmentationType.Start ||
                            frameFragmentation.FragmentType == NmFrameFragmentationType.Middle)
                            && (fragProt.IndexOf("TLS") == -1 || fragProt.IndexOf("SSL") == -1))
                        {
                            passed = false;
                            isReassembled = true;
                            reassemblyFilterSet = true;
                            isNextFrameSSL = true;
                        }
                        else if (frameFragmentation.FragmentType == NmFrameFragmentationType.End && fragProt.IndexOf("TLS") == -1 && fragProt.IndexOf("SSL") == -1)
                        {
                            this.reassembledFrame = this.insertedRawFrame;

                            errorValue = NetmonAPI.NmParseFrame(
                                        this.frameParser,
                                        this.insertedRawFrame,
                                        frameNumber,
                                        option,
                                        out this.parsedFrame,
                                        out this.insertedRawFrame);

                            HandleStack.Push(this.parsedFrame);
                            HandleStack.Push(this.insertedRawFrame);
                        }
                        else if (frameFragmentation.FragmentType == NmFrameFragmentationType.None)
                        {
                            reassemblyFilterSet = false;
                        }
                    }

                    if (passed && conversationFrame)
                    {
                        // We initialize once for regular and once for reassembled frames.  And it's
                        // possible for regular frames we have to re-initialize as TLS structures
                        // can change.
                        if (!reassemblyFilterSet)
                        {
                            if (!this.initNonReassembly)
                            {
                                ////Initializes Decryption Variables and Filterset
                                this.InitDecryptionVariables(
                                                this.parsedFrame,
                                                ref initFilter,
                                                ref this.cipherSuiteInfo);

                                if (initFilter.ToUpper().Contains("ESP"))
                                {
                                    this.isEspPresent = true;
                                }

                                // SslSmtp is a special case as the filter string can change.
                                if (!initFilter.Contains("SslSmtp.Ssl") && !initFilter.Contains("PayloadHeader") && !initFilter.Contains("PreLoginPacketData") && !cipherSuiteInfo.isSSLV2Selector)
                                {
                                    this.initNonReassembly = true;
                                }
                            }

                            // Set the filter string for this frame based on the initFilter string.
                            if (!SetFilterString(ref reassemblyFilterSet, initFilter))
                            {
                                ReleaseHandles(ref HandleStack);

                                return false;
                            }
                        }
                        else
                        {
                            if (!this.initReassembly)
                            {
                                ////Initializes Decryption Variables and Filterset
                                this.InitDecryptionVariables(
                                                this.parsedFrame,
                                                ref initReassemblyFilter,
                                                ref this.cipherSuiteInfo);

                                this.initReassembly = true;
                            }

                            // Set the filter string for this frame based on the initReassemblyFilter string.
                            if (!SetFilterString(ref reassemblyFilterSet, initReassemblyFilter))
                            {
                                ReleaseHandles(ref HandleStack);

                                return false;
                            }
                        }

                        // Get TCP Convesation ID for current frame so we can store some state for fields in Variables that require it.
                        unsafe
                        {
                            UInt64 tcpid;
                            uint outlen;
                            NmPropertyValueType propType;
                            byte* LenAsByte = (byte*)&tcpid;
                            errorValue = NetmonAPI.NmGetPropertyByName(this.frameParser, "Conversation.TCP.ID", 4, LenAsByte, out outlen, out propType, 0, null);
                            if (errorValue != (ulong)Status.ERROR_SUCCESS)
                            {
                                Variables.Log.LogMessage(
                                            "Error " + errorValue + "occured getting the TCP Property.");

                                ReleaseHandles(ref HandleStack);
                                return false;
                            }

                            Variables.TCPID = tcpid;

                            if (lastTCPID != Variables.TCPID)
                            {
                                Variables.Log.LogMessage("Changing Conversation ID from " + lastTCPID + " to " + Variables.TCPID);
                                lastTCPID = Variables.TCPID;

                                if (Variables.SSLTLSReassemblyPayload != null)
                                {
                                    Variables.Log.LogMessage("Warning, previously TLS/SSL payload fragment has been discarded.  " +
                                                            "Check to see that the conversation has ended properly.  " +
                                                            "Payload discarded was " + Variables.SSLTLSReassemblyPayload.Length);
                                    Variables.SSLTLSReassemblyPayload = null;
                                }
                            }
                        }

                        bool exitOnError = false;
                        bool result = false;

                        // Fragmentation that happens at TLS/SSL might not be properly handled by TCP.
                        // The TCP parsers depend on the push bit as a hint, however, this doesn't always
                        // allign with TLS/SSL fragments.  So we'll try to handle this in the expert.
                        IntPtr lastRawFrame = rawFrame;
                        if (reassembledFrame != (IntPtr)(-1) && reassembledFrame != IntPtr.Zero)
                        {
                            lastRawFrame = reassembledFrame;
                        }

                        if (this.IsTLSSSLPayloadFragmented(lastRawFrame, frameNumber))
                        {
                            // As this is a fragment, we expect the next frame to be SSL/TLS.
                            isNextFrameSSL = true;

                            // Save this payload and combine with a previous one if it exists.
                            // We need to get the payload length.
                            uint PayloadLength = GetPayloadLength(this.evalframeParser);

                            unsafe
                            {
                                byte[] TCPPayloadBuf = new byte[PayloadLength];
                                uint bufLen;

                                fixed (byte* buf = TCPPayloadBuf)
                                {
                                    errorValue = NetmonAPI.NmGetPartialRawFrame(lastRawFrame, Variables.TCPPayloadOffset, PayloadLength, buf, out bufLen);
                                }

                                if (Variables.SSLTLSReassemblyPayload == null)
                                {
                                    // Save the current payload
                                    Variables.SSLTLSReassemblyPayload = TCPPayloadBuf;
                                    Variables.Log.LogMessage((frameNumber+1) + ": Fragmented TLS/SSL new payload size=" + TCPPayloadBuf.Length);
                                }
                                else
                                {
                                    // Tack on this payload to the last.
                                    byte[] tmp = new byte[Variables.SSLTLSReassemblyPayload.Length];

                                    tmp = Variables.SSLTLSReassemblyPayload;
                                    Variables.SSLTLSReassemblyPayload = new byte[tmp.Length + PayloadLength];

                                    Array.Copy(tmp, Variables.SSLTLSReassemblyPayload, tmp.Length);
                                    Array.Copy(TCPPayloadBuf, 0, Variables.SSLTLSReassemblyPayload, tmp.Length, PayloadLength);

                                    Variables.Log.LogMessage((frameNumber + 1) + ": Fragmented TLS/SSL adding payload size=" + PayloadLength);
                                }
                            }
                        }
                        else // It's a good packet, so process it's fields as normal.
                        {
                            // Check if there is a previously collected payload to add to this one.
                            if (Variables.SSLTLSReassemblyPayload != null)
                            {
                                // We need to get the payload length.
                                uint PayloadLength = GetPayloadLength(this.evalframeParser);

                                unsafe
                                {
                                    byte[] TCPPayloadBuf = new byte[PayloadLength];
                                    uint bufLen;

                                    fixed (byte* buf = TCPPayloadBuf)
                                    {
                                        errorValue = NetmonAPI.NmGetPartialRawFrame(lastRawFrame, Variables.TCPPayloadOffset, PayloadLength, buf, out bufLen);
                                    }

                                    // Tack on this payload to the last.
                                    byte[] tmp = new byte[Variables.SSLTLSReassemblyPayload.Length];

                                    tmp = Variables.SSLTLSReassemblyPayload;
                                    Variables.SSLTLSReassemblyPayload = new byte[tmp.Length + PayloadLength];

                                    Array.Copy(tmp, Variables.SSLTLSReassemblyPayload, tmp.Length);
                                    Array.Copy(TCPPayloadBuf, 0, Variables.SSLTLSReassemblyPayload, tmp.Length, PayloadLength);

                                    Variables.Log.LogMessage((frameNumber + 1) + ": Fragmented TLS/SSL final payload size=" + PayloadLength);

                                }

                                // Now create a new reassembled frame to evaluate and push into the destination capture file.
                                byte[] rawSSLTLSReassembledFrame = null;

                                if (BuildRawSSLTLSReassembledFrame(out rawSSLTLSReassembledFrame, initFilter))
                                {
                                    // Build a new raw frame using the payload we collected and this new manufactured PayloadHeader
                                    byte[] reassembledData = new byte[rawSSLTLSReassembledFrame.Length + Variables.SSLTLSReassemblyPayload.Length];
                                    Array.Copy(rawSSLTLSReassembledFrame, reassembledData, rawSSLTLSReassembledFrame.Length);
                                    Array.Copy(Variables.SSLTLSReassemblyPayload, 0, reassembledData, rawSSLTLSReassembledFrame.Length, Variables.SSLTLSReassemblyPayload.Length);

                                    IntPtr rawSSLTLSFrame;

                                    GCHandle gch = GCHandle.Alloc(reassembledFrame, GCHandleType.Pinned);

                                    IntPtr bufferIndex = Marshal.UnsafeAddrOfPinnedArrayElement(reassembledData, 0);

                                    //ulong timestamp;
                                    //NetmonAPI.NmGetFrameTimeStamp(lastRawFrame, out timestamp);
                                    
                                    NM_TIME tm = new NM_TIME();
                                    tm.NmTimeStampSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(NM_TIME));
                                    NetmonAPI.NmGetFrameTimeStampEx(rawFrame, ref tm);

                                    errorValue = NetmonAPI.NmBuildRawFrameFromBufferEx(bufferIndex, (uint)reassembledData.Length, (uint)0xFFFC, ref tm, out rawSSLTLSFrame);

                                    gch.Free();

                                    if (errorValue != 0)
                                    {
                                        Variables.Log.LogMessage("Unable to build raw frame for manufactured Payload.  Please report bug.");
                                    }
                                    else
                                    {
                                        IntPtr parsedReassembledFrame;
                                        IntPtr insertedReassembledFrame;
                                        // Now parse this new raw frame.  This is what we'll pass to get evaluated.
                                        errorValue = NetmonAPI.NmParseFrame(frameParser,
                                                                            rawSSLTLSFrame,
                                                                            frameNumber,
                                                                            option,
                                                                            out parsedReassembledFrame,
                                                                            out insertedReassembledFrame);

                                        HandleStack.Push(rawSSLTLSFrame);
                                        HandleStack.Push(parsedReassembledFrame);
                                        HandleStack.Push(insertedReassembledFrame);

                                        if (errorValue != 0)
                                        {
                                        }
                                        else
                                        {
                                            this.reassembledFrame = rawSSLTLSFrame;

                                            reassemblyFilterSet = true;
                                            // Since the frame has changed, we have to reconfigure the filter set.
                                            if (!SetFilterString(ref reassemblyFilterSet, initReassemblyFilter))
                                            {
                                                return false;
                                            }

                                            //// packet is parsed and the frame fields will be enumerated.
                                            result = ParsedFrameInformation(
                                                                        parsedReassembledFrame,
                                                                        ref frameNumber,
                                                                        ref isKeyBlockComputed,
                                                                        out isDecryptedAppDataPacket,
                                                                        ref exitOnError,
                                                                        this.filter);
                                        }
                                    }
                                }

                                Variables.SSLTLSReassemblyPayload = null;
                            }
                            else
                            {
                                //// packet is parsed and the frame fields will be enumerated.
                                result = ParsedFrameInformation(
                                                            this.parsedFrame,
                                                            ref frameNumber,
                                                            ref isKeyBlockComputed,
                                                            out isDecryptedAppDataPacket,
                                                            ref exitOnError,
                                                            this.filter);
                            }

                            if (exitOnError)
                            {
                                break;
                            }

                            if (!result)
                            {
                                decryptionResult = Variables.Log.LastError;
                                return false;
                            }
                        }
                    }

                    //NetmonAPI.NmCloseHandle(this.insertedRawFrame);
                    //NetmonAPI.NmCloseHandle(this.parsedFrame);
                }

                if (conversationFrame)
                {
                    //// DecryptedCapFile Frame Number
                    if (Variables.previousFrameNumber != frameNumber)
                    {
                        this.decryptedFrameNumber++;
                    }

                    CaptureGeneration.CaptureFileGeneration(
                                            this.rawFrame,
                                            ref frameNumber,
                                            ref this.frameSource,
                                            ref this.decryptedFrameNumber,
                                            ref this.framesInserted,
                                            ref isReassembled,
                                            this.isEspPresent,
                                            this.reassembledFrame,
                                            this.createdCaptureFile,
                                            Variables.decryptedApplicationData,
                                            isDecryptedAppDataPacket,
                                            this.cipherSuiteInfo);
                }

                //NetmonAPI.NmCloseHandle(this.rawFrame);     
                ReleaseHandles(ref HandleStack);
            }

            if (!this.initNonReassembly && !this.initReassembly)
            {
                decryptionResult = "SSL frames are not found in the current capture file";
                Variables.Log.LogMessage(decryptionResult);
                return false;
            }

            // TODO: Think this is counting reassembled frames, which means this can be inaccurate, would need to count that no decrypted frames are found
            if (this.framesInserted == 0)
            {
                // TODO: Need to separate supported engine and parser version
                decryptionResult = "No Frames were decrypted," +
                    " Netmon Filter Set may not match with current parser version.  Use parser version " + Constants.SupportedNetmonParserVersion + " or greater.";
                Variables.Log.LogMessage(decryptionResult);
                return false;
            }

            return true;
        }

        private static bool CheckVersions()
        {
            ushort major;
            ushort minor;
            ushort build;
            ushort revision;
            uint errorValue;

            NetmonAPI.NmGetApiVersion(
                                out major,
                                out minor,
                                out build,
                                out revision);

            // Check Minor and Build version of engine.
            if (minor == Constants.SupportedNetmonMinorVersion && build >= Constants.SupportedNetmonBuildVersion)
            {
                Variables.Log.LogMessage("This Netmon Version is supported");
            }
            else
            {
                Variables.Log.LogMessage(
                            "****Warning****: Network Monitor  Version: "
                            + major + "." + minor + "." + build + "." + revision
                            + " may cause Expert to fail.  Please use "
                            + Constants.SupportedNetmonMinorString + " or greater.");
            }

            // Check Parser Version
            uint guidLen = 50;
            String parserGuid = new String('0', (int)guidLen);

            errorValue = NetmonAPI.NmGetActiveNplProfileGuid(ref guidLen, parserGuid);
            if (errorValue != 0)
            {
                Variables.Log.LogMessage("****Error***: Unalbe to get Profile Guid.  Error=" + errorValue);
                return false;
            }

            uint packageVerLen = 20;
            String packageVer = new String('0', (int)packageVerLen);
            errorValue = NetmonAPI.NmGetNplProfileAttributeByGuid(parserGuid, NmNplProfileAttribute.NmNplProfileAttributePackageVersion, ref packageVerLen, packageVer);
            if (errorValue != 0)
            {
                Variables.Log.LogMessage("****Error***: Unalbe to get Profile Guid Version.  Error=" + errorValue);
                return false;
            }

            if (!packageVer.Contains(Constants.SupportedNetmonParserVersion))
            {
                Variables.Log.LogMessage("****Warning***: We've tested with parser version: " + Constants.SupportedNetmonParserVersion 
                    + ".  Your version is: " + packageVer + ". This might cause problems if the TLS/SSL parsers have changed significantly.");
            }

            return true;
        }

        /// PaulLo
        /// <summary>
        /// Release the handles using Netmon CloseHandle API.  Allows handles to be closed in the order
        /// they were opened.
        /// </summary>
        /// <param name="Handles">Handles stored in a Stack object.</param>
        private void ReleaseHandles(ref Stack Handles)
        {
            Debug.WriteLine("Closing:");

            IntPtr h;
            while (Handles.Count > 0)
            {
                h = (IntPtr)Handles.Pop();
                Debug.WriteLine(string.Format(" {0:X}", (int)h));
                NetmonAPI.NmCloseHandle(h);
            }
        }

        /// PaulLo
        /// <summary>
        /// Checks to see if TLS/SSL fragmentation is occuring.
        /// </summary>
        /// <param name="rawFrame">raw frame to analyze</param>
        /// <param name="frameNumber">frame number of frame to be analzyed</param>
        private bool IsTLSSSLPayloadFragmented(IntPtr rawFrame, uint frameNumber)
        {
            Stack handleStack = new Stack();

            Variables.Log.LogMessage(".................................................");
            Variables.Log.LogMessage("Entered IsTLSSLPayloadFragmented: Frame " + (frameNumber + 1));
            Variables.Log.LogMessage(".................................................");

            // Keeps track if we have a partial frame due to an imcomplete TlsRecordLayer
            bool RecordLayerStart = false;

            // Don't parse any frames twice.
            if (frameNumber == Variables.previousFrameNumber)
            {
                return false;
            }

            NmFrameParsingOption option = (NmFrameParsingOption.FieldDisplayStringRequired |
                                    NmFrameParsingOption.FieldFullNameRequired |
                                    NmFrameParsingOption.DataTypeNameRequired);

            IntPtr targetParsedFrame, parsedFrame, insertedRawFrame = IntPtr.Zero;
            IntPtr reassembledParsedFrame = IntPtr.Zero;

            uint errorValue = NetmonAPI.NmParseFrame(
                            this.evalframeParser,
                            rawFrame,
                            frameNumber,
                            option,
                            out parsedFrame,
                            out insertedRawFrame);

            handleStack.Push(parsedFrame);
            handleStack.Push(insertedRawFrame);

            if (errorValue == (ulong)Status.ERROR_SUCCESS)
            {
                bool conversationFrame = true;

                // Now check if it's a conversation that is selected.
                if (!string.IsNullOrEmpty(this.conversationFilter))
                {
                    errorValue = NetmonAPI.NmEvaluateFilter(
                                           parsedFrame,
                                           this.evalConversationFilterId,
                                           out conversationFrame);

                    if (errorValue != (ulong)Status.ERROR_SUCCESS)
                    {
                        Variables.Log.LogMessage(
                                    "Error occured in retrieving" +
                                    " the state of the Conversation Filter");
                        return false;
                    }
                }

                if (conversationFrame)
                {
                    targetParsedFrame = parsedFrame;

                    NmFragmentationInfo frameFragmentation = new NmFragmentationInfo();
                    errorValue = NetmonAPI.NmGetFrameFragmentInfo(this.parsedFrame, ref frameFragmentation);

                    // If it's detected as a TCP fragement, then don't even evaluate.
                    string fragProt = new string(frameFragmentation.FragmentedProtocolName).Replace("\0", string.Empty);
                    if (fragProt.IndexOf("TLS") == -1)
                    {
                        if (frameFragmentation.FragmentType != NmFrameFragmentationType.None)
                        {
                            this.ReleaseHandles(ref handleStack);

                            // It's a TCP fragment, so don't try and evaluate now.
                            return false;
                        }
                        else if (frameFragmentation.FragmentType == NmFrameFragmentationType.End && insertedRawFrame != (IntPtr)(-1))
                        {
                            IntPtr newinsertedRawFrame;
                            evalReassembledFrameCount++;

                            errorValue = NetmonAPI.NmParseFrame(
                                            this.evalframeParser,
                                            insertedRawFrame,
                                            frameNumber + evalReassembledFrameCount,
                                            option,
                                            out reassembledParsedFrame,
                                            out newinsertedRawFrame);

                            handleStack.Push(reassembledParsedFrame);
                            handleStack.Push(newinsertedRawFrame);

                            targetParsedFrame = reassembledParsedFrame;
                        }
                    }

                    Debug.WriteLine("");
                    Debug.WriteLine("=============================================");
                    Debug.WriteLine("Frame: " + (frameNumber + 1) + " - " + (frameNumber + evalReassembledFrameCount));
                    Debug.WriteLine("=============================================");

                    ushort TLSRecordLength = 0;

                    uint fieldCount;

                    bool ret = false;

                    NetmonAPI.NmGetFieldCount(targetParsedFrame, out fieldCount);
                    for (uint fid = 0; fid < fieldCount; fid++)
                    {
                        // For Debug only

                        char[] desc = new char[2048];

                        unsafe
                        {
                            fixed (char* pstr = desc)
                            {
                                errorValue = NetmonAPI.NmGetFieldName(
                                                            parsedFrame,
                                                            fid,
                                                            NmParsedFieldNames.FieldDisplayString,
                                                            2048,
                                                            pstr);
                            }
                        }

                        string fieldDisplayString = new string(desc).Replace("\0", string.Empty);

                        char[] namePath = new char[2048];

                        unsafe
                        {
                            fixed (char* pstr = namePath)
                            {
                                errorValue = NetmonAPI.NmGetFieldName(
                                                            targetParsedFrame,
                                                            fid,
                                                            NmParsedFieldNames.NamePath,
                                                            2048,
                                                            pstr);
                            }
                        }

                        string namePathString = new string(namePath).Replace("\0", string.Empty);

                        int idx = namePathString.LastIndexOf(".");
                        if (idx < 0)
                        {
                            idx = 0;
                        }
                        else
                        {
                            idx++;
                        }

                        Debug.WriteLine(fid + ":" + namePathString.Substring(idx) + ": " + fieldDisplayString);

                        // Look for certain fields to save for later

                        if (namePathString.EndsWith(".Ipv6.SourceAddress", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("IPv6Properties.SourceAddress", StringComparison.OrdinalIgnoreCase))
                        {
                            uint retlen;

                            unsafe
                            {
                                fixed (byte* buf = Variables.IPv6SourceAddress)
                                {
                                    NetmonAPI.NmGetFieldInBuffer(parsedFrame, fid, (uint)16, buf, out retlen);
                                }
                            }

                            //Array.Reverse(Variables.IPv6SourceAddress);
                        }

                        if (namePathString.EndsWith(".Ipv6.DestinationAddress", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("IPv6Properties.DestinationAddress", StringComparison.OrdinalIgnoreCase))
                        {
                            uint retlen;

                            unsafe
                            {
                                fixed (byte* buf = Variables.IPv6DestinationAddress)
                                {
                                    NetmonAPI.NmGetFieldInBuffer(parsedFrame, fid, (uint)16, buf, out retlen);
                                }
                            }

                            //Array.Reverse(Variables.IPv6DestinationAddress);
                        }

                        if (namePathString.EndsWith(".Ipv4.SourceAddress", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("IPv4Properties.SourceAddress", StringComparison.OrdinalIgnoreCase))
                        {
                            uint retlen;

                            unsafe
                            {
                                fixed (byte* buf = Variables.IPv4SourceAddress)
                                {
                                    NetmonAPI.NmGetFieldInBuffer(parsedFrame, fid, (uint)4, buf, out retlen);
                                }
                            }

                            //Array.Reverse(Variables.IPv4SourceAddress);
                        }

                        if (namePathString.EndsWith(".Ipv4.DestinationAddress", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("IPv4Properties.DestinationAddress", StringComparison.OrdinalIgnoreCase))
                        {
                            uint retlen;

                            unsafe
                            {
                                fixed (byte* buf = Variables.IPv4DestinationAddress)
                                {
                                    NetmonAPI.NmGetFieldInBuffer(parsedFrame, fid, (uint)4, buf, out retlen);
                                }
                            }

                            //Array.Reverse(Variables.IPv4DestinationAddress);
                        }

                        if (namePathString.EndsWith(".Tcp.DstPort", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("TCPProperties.DestinationPort", StringComparison.OrdinalIgnoreCase))
                        {
                            NetmonAPI.NmGetFieldValueNumber16Bit(parsedFrame, fid, out Variables.TCPDstPort);
                        }

                        if (namePathString.EndsWith(".Tcp.SrcPort", StringComparison.OrdinalIgnoreCase) ||
                            namePathString.EndsWith("TCPProperties.SourcePort", StringComparison.OrdinalIgnoreCase))
                        {
                            NetmonAPI.NmGetFieldValueNumber16Bit(parsedFrame, fid, out Variables.TCPSrcPort);
                        }

                        if (namePathString.IndexOf("PayloadHeader.PayloadLength") != -1)
                        {
                            NetmonAPI.NmGetFieldValueNumber32Bit(targetParsedFrame, fid, out Variables.ReassemblyPayloadLength);
                        }

                        if (namePathString.Substring(namePathString.Length - 3, 3) == "TLS")
                        {
                            // Save TCP Payload Offset for later when we need to extract the payload.
                            uint fieldSize;
                            NetmonAPI.NmGetFieldOffsetAndSize(targetParsedFrame, fid, out Variables.TCPPayloadOffset, out fieldSize);
                            Variables.TCPPayloadOffset /= 8;
                        }

                        if (namePathString.IndexOf("TlsRecordLayer.Length") != -1)
                        {
                            NetmonAPI.NmGetFieldValueNumber16Bit(targetParsedFrame, fid, out TLSRecordLength);
                            ret = false;
                        }

                        if (namePathString.IndexOf("TlsRecordLayer.ApplicationData.SSLApplicationData") != -1)
                        {
                            uint fieldOffset, fieldSize;
                            NetmonAPI.NmGetFieldOffsetAndSize(targetParsedFrame, fid, out fieldOffset, out fieldSize);

                            if (fieldSize / 8 != TLSRecordLength)
                            {
                                if ((uint)insertedRawFrame != UInt32.MaxValue)
                                {
                                    NetmonAPI.NmCloseHandle(reassembledParsedFrame);
                                    NetmonAPI.NmCloseHandle(insertedRawFrame);
                                }
                                NetmonAPI.NmCloseHandle(parsedFrame);

                                Variables.TLSSSLUnassignedPayloadTotal = fieldSize / 8;
                                Variables.LastTLSRecordLength = TLSRecordLength;

                                Variables.Log.LogMessage("Fragmented: Frame " + (frameNumber+1) + " Size " + TLSRecordLength + " Remaining " + fieldSize/8);

                                ret = true;
                            }
                        }

                        if (namePathString.IndexOf("TlsRecLayer.ContinuedTLVData") != -1)
                        {
                            uint fieldOffset, fieldSize;
                            NetmonAPI.NmGetFieldOffsetAndSize(targetParsedFrame, fid, out fieldOffset, out fieldSize);

                            Variables.TLSSSLUnassignedPayloadTotal += fieldSize / 8;

                            if (Variables.TLSSSLUnassignedPayloadTotal == Variables.LastTLSRecordLength)
                            {
                                Variables.Log.LogMessage("Fragmented: Frame " + (frameNumber+1) + " Size " + fieldSize/8 + " Complete, total " + Variables.TLSSSLUnassignedPayloadTotal);
                                ret = false;
                            }
                            else
                            {
                                Variables.Log.LogMessage("Fragmented: Frame " + (frameNumber+1) + " Size " + fieldSize/8 + " Not Complete, total " + Variables.TLSSSLUnassignedPayloadTotal);
                                ret = true;
                            }
                        }

                        // When we find the start of a TLS Record, record it so we can make sure we have a complete entry.
                        // Sometimes we'll get just the header and no data or record length.
                        if (namePathString.EndsWith("TlsRecordLayer"))
                        {
                            RecordLayerStart = true;
                        }

                        if (namePathString.EndsWith("SSLApplicationData"))
                        {
                            RecordLayerStart = false;
                        }
                    }

                    this.ReleaseHandles(ref handleStack);

                    return ret;
                }
            }

            this.ReleaseHandles(ref handleStack);

            // if we got to this point, we only have a partial fragmented frame if we got a reocrd start with out the end.
            if (RecordLayerStart)
            {
                Variables.Log.LogMessage("Fragmented: Frame " + (frameNumber + 1) + " Incomplete RecordLayerStart.");
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// PaulLo: Creates a raw frame with a reassembled header.
        /// </summary>
        /// <param name="frameParser">
        /// Frame Parser to retreive property from.
        /// </param>
        uint GetPayloadLength(IntPtr frameParser)
        {
            if (Variables.ReassemblyPayloadLength == 0)
            {
                unsafe
                {
                    uint retLen;
                    NmPropertyValueType propType;
                    byte[] TCPPayloadLengthBuf = new byte[4];

                    fixed (byte* buf = TCPPayloadLengthBuf)
                    {
                        uint errorValue = NetmonAPI.NmGetPropertyByName(frameParser, "property.TCPPayloadLength", 4, buf, out retLen, out propType, 0, null);
                    }

                    return BitConverter.ToUInt32(TCPPayloadLengthBuf, 0);
                }
            }
            else
            {
                return Variables.ReassemblyPayloadLength;
            }
        }

        /// <summary>
        /// PaulLo: Creates a raw frame with a reassembled header.
        /// </summary>
        /// <param name="rawframebuf">
        /// Raw frame buffer to return.
        /// </param>
        bool BuildRawSSLTLSReassembledFrame(out byte[] rawframebuf, string initFilter)
        {
            Variables.PayloadHeaderLen = Constants.RawPayloadHeaderSize;
            ushort protocolCount = 0;

            if (initFilter.ToLower().Contains("ipv4"))
            {
                Variables.PayloadHeaderLen += Constants.PayloadHeader_IPv4ProtBlockSize;
                protocolCount++;
            }
            if (initFilter.ToLower().Contains("ipv6"))
            {
                Variables.PayloadHeaderLen += Constants.PayloadHeader_IPv6ProtBlockSize;
                protocolCount++;
            }
            if (initFilter.ToLower().Contains("esp"))
            {
                Variables.PayloadHeaderLen += Constants.PayloadHeader_ESPProtBlockSize;
                protocolCount++;
            }
            if (initFilter.ToLower().Contains("tcp"))
            {
                Variables.PayloadHeaderLen += Constants.PayloadHeader_TCPProtBlockSize;
                protocolCount++;
            }

            rawframebuf = new byte[Variables.PayloadHeaderLen];
            byte[] PayloadHeaderLenArray = BitConverter.GetBytes(Variables.PayloadHeaderLen);
            Array.Reverse(PayloadHeaderLenArray);

            // Version - 0
            Array.Copy(BitConverter.GetBytes((UInt16)0x0002), 0, rawframebuf, 0, sizeof(UInt16));
            // HeaderLength - 2
            Array.Copy(PayloadHeaderLenArray, 2, rawframebuf, 2, sizeof(UInt16));
            // PayloadType - 4
            Array.Copy(BitConverter.GetBytes((byte)1), 0, rawframebuf, 4, sizeof(byte));
            // ReassembledProtocol - 5 "TCP"
            Array.Copy(BitConverter.GetBytes((UInt64)0x5000430054), 0, rawframebuf, 5, sizeof(UInt64));
            // RStatus - 13
            Array.Copy(BitConverter.GetBytes((UInt32)0), 0, rawframebuf, 13, sizeof(UInt32));
            // LowerProtocolCount - 17
            Array.Copy(BitConverter.GetBytes((byte)protocolCount), 0, rawframebuf, 17, sizeof(byte));

            // Keep track of the offset as it can be variable from here on.
            uint phoffset = 18;

            if (initFilter.ToLower().Contains("ipv4"))
            {
                // Protocol Block IPv4

                // Get IP address for ConversationKey and Properties
                //byte[] IPAddrClient;
                //Utils.GetIpFromString(Variables.clientIP, out IPAddrClient);

                //byte[] IPAddrServer;
                //Utils.GetIpFromString(Variables.serverIP, out IPAddrServer);

                // Protocol Name - 18 IPv4
                Array.Copy(new byte[10] { 0x49, 0x00, 0x50, 0x00, 0x76, 0x00, 0x34, 0x00, 0x00, 0x00 }, 0, rawframebuf, phoffset, 10);
                phoffset += 10;

                // IPv4 ConversatioKeyLength - 28
                Array.Copy(BitConverter.GetBytes((UInt32)0x08000000), 0, rawframebuf, phoffset, sizeof(UInt32));
                phoffset += sizeof(UInt32);

                //UInt32 ClientIP = BitConverter.ToUInt32(IPAddrClient, 0);
                //UInt32 ServerIP = BitConverter.ToUInt32(IPAddrServer, 0);

                //Array.Reverse(IPAddrServer);
                //Array.Reverse(IPAddrClient);

                byte[] IPv4DestinationAddressRev = new byte[4];
                byte[] IPv4SourceAddressRev = new byte[4];

                Array.Copy(Variables.IPv4DestinationAddress, IPv4DestinationAddressRev, Variables.IPv4DestinationAddress.Length);
                Array.Copy(Variables.IPv4SourceAddress, IPv4SourceAddressRev, Variables.IPv4SourceAddress.Length);

                Array.Reverse(IPv4DestinationAddressRev);
                Array.Reverse(IPv4SourceAddressRev);

                if (CompIP(Variables.IPv4SourceAddress, Variables.IPv4DestinationAddress))
                {
                    // Conversation Keys - 32
                    Array.Copy(IPv4SourceAddressRev, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);

                    // Conversation Keys - 36
                    Array.Copy(IPv4DestinationAddressRev, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);

                    // IPv4 PropertyBlockLength - 40
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0C00), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address Descriptor - 42
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0404), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address - 44
                    Array.Copy(Variables.IPv4SourceAddress, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);
                        
                    // Destination Address Descriptor - 48
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0404), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);
                        
                    // Destination Address - 50
                    Array.Copy(Variables.IPv4DestinationAddress, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);
                }
                else
                {
                    // Conversation Keys - 32
                    Array.Copy(IPv4DestinationAddressRev, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);

                    // Conversation Keys - 36
                    Array.Copy(IPv4SourceAddressRev, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);

                    // IPv4 PropertyBlockLength - 40
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0C00), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address Descriptor - 42
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0404), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address - 44
                    Array.Copy(Variables.IPv4DestinationAddress, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);

                    // Destination Address Descriptor - 48
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0404), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Destination Address - 50
                    Array.Copy(Variables.IPv4SourceAddress, 0, rawframebuf, phoffset, sizeof(UInt32));
                    phoffset += sizeof(UInt32);
                }
            }

            if (initFilter.ToLower().Contains("ipv6"))
            {
                // IPv6
                // Protocol Name - 18 IPv4
                Array.Copy(new byte[10] { 0x49, 0x00, 0x50, 0x00, 0x76, 0x00, 0x36, 0x00, 0x00, 0x00 }, 0, rawframebuf, phoffset, 10);
                phoffset += 10;

                // IPv4 ConversatioKeyLength - 28
                Array.Copy(BitConverter.GetBytes((UInt32)0x20000000), 0, rawframebuf, phoffset, sizeof(UInt32));
                phoffset += sizeof(UInt32);

                byte[] IPv6DestinationAddressRev = new byte[16] ;
                byte[] IPv6SourceAddressRev = new byte[16];

                Array.Copy(Variables.IPv6DestinationAddress, IPv6DestinationAddressRev, Variables.IPv6DestinationAddress.Length);
                Array.Copy(Variables.IPv6SourceAddress, IPv6SourceAddressRev, Variables.IPv6SourceAddress.Length);

                //Array.Reverse(IPv6DestinationAddressRev);
                //Array.Reverse(IPv6SourceAddressRev);

                if (CompIP(Variables.IPv6SourceAddress, Variables.IPv6DestinationAddress))
                {
                    // Conversation Keys - 32
                    Array.Copy(IPv6DestinationAddressRev, 0, rawframebuf, phoffset, 16);
                    phoffset += 16;

                    // Conversation Keys - 36
                    Array.Copy(IPv6SourceAddressRev, 0, rawframebuf, phoffset, 16);
                    phoffset += 16;

                    // IPv4 PropertyBlockLength - 40
                    Array.Copy(BitConverter.GetBytes((UInt16)0x2700), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address Descriptor - 42
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0410), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Address - 44
                    Array.Copy(Variables.IPv6SourceAddress, 0, rawframebuf, phoffset, Variables.IPv6SourceAddress.Length);
                    phoffset += (uint)Variables.IPv6SourceAddress.Length;

                    // Destination Address Descriptor - 48
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0410), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Destination Address - 50
                    Array.Copy(Variables.IPv6DestinationAddress, 0, rawframebuf, phoffset, Variables.IPv6DestinationAddress.Length);
                    phoffset += (uint)Variables.IPv6DestinationAddress.Length;
                }
                else
                {
                    // Conversation Keys - 32
                    Array.Copy(IPv6SourceAddressRev, 0, rawframebuf, phoffset, 16);
                    phoffset += 16;

                    // Conversation Keys - 36
                    Array.Copy(IPv6DestinationAddressRev, 0, rawframebuf, phoffset, 16);
                    phoffset += 16;

                    // IPv4 PropertyBlockLength - 40
                    Array.Copy(BitConverter.GetBytes((UInt16)0x2700), 0, rawframebuf, phoffset, sizeof(UInt16));

                    // Source Address Descriptor - 42
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0410), 0, rawframebuf, phoffset, sizeof(UInt16));

                    // Source Address - 44
                    Array.Copy(Variables.IPv6DestinationAddress, 0, rawframebuf, phoffset, Variables.IPv6DestinationAddress.Length);
                    phoffset += (uint)Variables.IPv6DestinationAddress.Length;

                    // Destination Address Descriptor - 48
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0410), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Destination Address - 50
                    Array.Copy(Variables.IPv6SourceAddress, 0, rawframebuf, phoffset, Variables.IPv6SourceAddress.Length);
                    phoffset += (uint)Variables.IPv6SourceAddress.Length;
                }

                // Destination Address Descriptor - 48
                Array.Copy(BitConverter.GetBytes((UInt16)0x0410), 0, rawframebuf, phoffset, sizeof(UInt16));
                phoffset += sizeof(UInt16);

                // Add final reserved 0xFF
                Array.Copy(BitConverter.GetBytes((byte)0xFF), 0, rawframebuf, phoffset, sizeof(byte));
                phoffset += sizeof(byte);
            }

            if (initFilter.ToLower().Contains("esp"))
            {
                // Now do TCP Block Properties
                // ESP Protocol Name - ESP
                Array.Copy(new byte[8] { 0x45, 0x00, 0x53, 0x00, 0x50, 0x00, 0x00, 0x00 }, 0, rawframebuf, phoffset, 8);
                phoffset += 8;

                // ESP ConversatioKeyLength
                Array.Copy(BitConverter.GetBytes((UInt32)0x00000000), 0, rawframebuf, phoffset, sizeof(UInt32));
                phoffset += sizeof(UInt32);

                // ESP PropertyBlockLength
                Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                phoffset += sizeof(UInt16);
            }

            if (initFilter.ToLower().Contains("tcp"))
            {
                // Now do TCP Block Properties
                // TCP Protocol Name - 54 TCP
                Array.Copy(new byte[8] { 0x54, 0x00, 0x43, 0x00, 0x50, 0x00, 0x00, 0x00 }, 0, rawframebuf, phoffset, 8);
                phoffset += 8;

                // TCP ConversatioKeyLength - 62
                Array.Copy(BitConverter.GetBytes((UInt32)0x04000000), 0, rawframebuf, phoffset, sizeof(UInt32));
                phoffset += sizeof(UInt32);

                byte[] TCPSrcPortArray = new byte[8];
                Array.Copy(BitConverter.GetBytes(Variables.TCPSrcPort), TCPSrcPortArray, 2);
                Array.Reverse(TCPSrcPortArray);

                byte[] TCPDstPortArray = new byte[8];
                Array.Copy(BitConverter.GetBytes(Variables.TCPDstPort), TCPDstPortArray, 2);
                Array.Reverse(TCPDstPortArray);

                if (Variables.TCPSrcPort < Variables.TCPDstPort)
                {
                    // PropertyBlockLength - 66
                    Array.Copy(BitConverter.GetBytes(Variables.TCPDstPort), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // PropertyBlockLength - 68
                    Array.Copy(BitConverter.GetBytes(Variables.TCPSrcPort), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // TCP PropertyBlockLength - 70
                    Array.Copy(BitConverter.GetBytes((UInt16)0x3200), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Port Descriptor - 72
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // PropertyBlockLength - 74
                    Array.Copy(TCPSrcPortArray, 0, rawframebuf, phoffset, sizeof(UInt64));
                    phoffset += sizeof(UInt64);
                    
                    // Source Port Descriptor - 82
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);
                    
                    // PropertyBlockLength - 84
                    Array.Copy(TCPDstPortArray, 0, rawframebuf, phoffset, sizeof(UInt64));
                    phoffset += sizeof(UInt64);
                }
                else
                {
                    // PropertyBlockLength - 66
                    Array.Copy(BitConverter.GetBytes(Variables.TCPSrcPort), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // PropertyBlockLength - 68
                    Array.Copy(BitConverter.GetBytes(Variables.TCPDstPort), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // TCP PropertyBlockLength - 70
                    Array.Copy(BitConverter.GetBytes((UInt16)0x3200), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // Source Port Descriptor - 72
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);

                    // PropertyBlockLength - 74
                    Array.Copy(TCPDstPortArray, 0, rawframebuf, phoffset, sizeof(UInt64));
                    phoffset += sizeof(UInt64);
                    
                    // Source Port Descriptor - 82
                    Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                    phoffset += sizeof(UInt16);
                    
                    // PropertyBlockLength - 84
                    Array.Copy(TCPSrcPortArray, 0, rawframebuf, phoffset, sizeof(UInt64));
                    phoffset += sizeof(UInt64);
                }

                // Don't care about SeqNumber and flags, hope nobody else does :)
                // SeqNum Descriptor - 92
                Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                phoffset += sizeof(UInt16);
                // SeqNum - 94
                Array.Copy(BitConverter.GetBytes((UInt64)0x0000), 0, rawframebuf, phoffset, sizeof(UInt64));
                phoffset += sizeof(UInt64);
                // NextSeqNum Descriptor - 102
                Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                phoffset += sizeof(UInt16);
                // NextSeqNum - 104
                Array.Copy(BitConverter.GetBytes((UInt64)0x0000), 0, rawframebuf, phoffset, sizeof(UInt64));
                phoffset += sizeof(UInt64);
                // TcpFlags Descriptor - 112
                Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
                phoffset += sizeof(UInt16);
                // TcpFlags - 114
                Array.Copy(BitConverter.GetBytes((UInt64)0x0000), 0, rawframebuf, phoffset, sizeof(UInt64));
                phoffset += sizeof(UInt64);
            }

            // Frame Count - 122
            Array.Copy(BitConverter.GetBytes((UInt16)0x0000), 0, rawframebuf, phoffset, sizeof(UInt16));
            phoffset += sizeof(UInt16);

            byte[] PayloadHeaderLength = new byte[4];
            PayloadHeaderLength = BitConverter.GetBytes((UInt32)Variables.SSLTLSReassemblyPayload.Length);
            Array.Reverse(PayloadHeaderLength);
            // Payload Length - 124
            Array.Copy(PayloadHeaderLength, 0, rawframebuf, phoffset, sizeof(UInt32));
            phoffset += sizeof(UInt32);

            // Contained Protocol - 128 TCP
            Array.Copy(new byte[38] { 0x53, 0x00, 0x53, 0x00, 0x4C, 0x00, 0x56, 0x00, 
                                        0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 
                                        0x6F, 0x00, 0x6E, 0x00, 0x53, 0x00, 0x65, 0x00, 
                                        0x6C, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 
                                        0x6F, 0x00, 0x72, 0x00, 0x00, 0x00 },
                0, rawframebuf, phoffset, 38);
            phoffset += 38;

            return true;
        }

        /// <summary>
        /// returns true if A is > B.
        /// </summary>
        /// <param name="instr">String that contains the IP address</param>
        /// <param name="addr">byte array with returned IP address</param>
        /// <param name="size">Size of returned byte array</param>
        private bool CompIP(byte[] IP_A, byte[] IP_B)
        {
            for (uint i = 0; i < IP_A.Length; i++)
            //for (uint i = (uint)(IP_A.Length - 1); i >= 0; i--)
                {
                if (IP_A[i] > IP_B[i])
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// returns the bytes for a 4 or 16 byte IP address.
        /// </summary>
        /// <param name="instr">String that contains the IP address</param>
        /// <param name="addr">byte array with returned IP address</param>
        /// <param name="size">Size of returned byte array</param>
        private void GetIPAddrFromString(string instr, out byte[] addr)
        {
            // We'll assume that if it has dot's in it, it's IPv4.
            if (instr.IndexOf('.') != -1)
            {             
                addr = new byte[4];
                uint count = 0;

                string[] octects = instr.Split(new char[] {'.'});
                foreach (string octect in octects)
                {
                    addr[count++] = byte.Parse(octect);
                }
            }
            else // It must be IPv6
            {
                addr = new byte[16];
            }
        }

        /// <summary>
        /// Initializes the filter string based on the version and if reassembly is set.
        /// </summary>
        /// <param name="reassemblyFilterSet">true if this is a reassembled frame.</param>
        /// <param name="initFilter">The filter prefix based on the hardware layer.</param>
        private bool SetFilterString(ref bool reassemblyFilterSet, string initFilter)
        {
            if (this.cipherSuiteInfo.SslVersion.Major == 3 && !(initFilter.Contains("SslSmtp") && this.currentState == CurrentState.Initial) && !cipherSuiteInfo.isSSLV2Selector)
            {
                switch (this.cipherSuiteInfo.SslVersion.Minor)
                {
                    case 0:
                        /*if (reassemblyFilterSet == true)
                        {
                            this.filter = new NMFilters(Constants.ReassembledSslV3);
                            reassemblyFilterSet = false;
                        }
                        else
                        {*/

                            this.filter = new NMFilters(initFilter + Constants.VersionSSL);
                            Variables.Log.LogMessage("SetFilterString1: " + this.filter);
                        //}

                        break;
                    case 1:
                        /*if (reassemblyFilterSet == true)
                        {
                            this.filter = new NMFilters(Constants.ReassembledTlsV1);
                            reassemblyFilterSet = false;
                        }
                        else
                        {*/
                            bool passed;
                            NetmonAPI.NmEvaluateFilter(
                                            this.parsedFrame,
                                            this.SSLVersionId,
                                            out passed);

                            if (passed)
                            {
                                string initFileMinusTLS = initFilter.Substring(0, initFilter.LastIndexOf("Tls."));
                                this.filter = new NMFilters(initFileMinusTLS + "SSL." + Constants.VersionSSL2dot0);
                                Variables.Log.LogMessage("SetFilterString2: " + this.filter);
                            }
                            else
                            {
                                this.filter = new NMFilters(initFilter + Constants.VersionTLS);
                                Variables.Log.LogMessage("SetFilterString3: " + this.filter);
                            }
                        //}

                        break;
                    case 2:
                        /*if (reassemblyFilterSet == true)
                        {
                            this.filter = new NMFilters(Constants.ReassembledTlsV1dot1);
                            reassemblyFilterSet = false;
                        }
                        else
                        {*/
                            this.filter = new NMFilters(initFilter + Constants.VersionTLS1dot1);
                            Variables.Log.LogMessage("SetFilterString4: " + this.filter);
                        //}

                        break;
                    default:
                        Variables.Log.LogMessage("This SSL version is not supported.");
                        return false;
                        break;
                }
            }
            else if (((this.cipherSuiteInfo.SslVersion.Major == 0) &&
                        (this.cipherSuiteInfo.SslVersion.Minor == 2)) || 
                        (this.currentState == CurrentState.Initial  && initFilter.Contains("SslSmtp")) ||
                        cipherSuiteInfo.isSSLV2Selector)
            {
                /*if (reassemblyFilterSet == true)
                {
                    this.filter = new NMFilters("PayloadHeader.Ssl." + Constants.VersionSSL2dot0);
                    reassemblyFilterSet = false;
                }
                else
                {*/
                this.filter = new NMFilters(initFilter + Constants.VersionSSL2dot0);
                Variables.Log.LogMessage("SetFilterString5: " + this.filter);
                //}
            }

            return true;
        }

        /// <summary>
        /// Initializes the configuration properties.
        /// </summary>
        /// <param name="properties">
        /// Dictionary of Properties.
        /// </param>
        private void InitializeConfigProperties(Dictionary<string, string> properties)
        {                       
            this.encryptedCapFile = properties["EncryptedCaptureFile"];
            this.certificateFilePath = properties["Certificate"];
            this.certificatePassword = properties["CertificatePassword"];            
            this.decryptedCapFile = properties["DecryptedCaptureFile"];                      
            Variables.Log = new Logger(properties["DebugLog"]);
            this.conversationFilter = properties["ConversationFilter"];            
        }        
    }
}